package com.roadmap.biz.sns;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.roadmap.biz.common.BaseManager;
import com.roadmap.biz.common.RdcQueryConst;
import com.roadmap.common.BaseConst;
import com.roadmap.common.BaseEntity;
import com.roadmap.common.RdcLogger;
import com.roadmap.entities.Category;
import com.roadmap.entities.Friendship;
import com.roadmap.entities.ext.FriendExt;
import com.roadmap.exception.ActionNotSupportedException;
import com.roadmap.exception.EntityNotFoundException;
import com.roadmap.exception.InvalidActionException;
import com.roadmap.exception.InvalidQueryException;
import com.roadmap.exception.InvalidStatusException;
import com.roadmap.exception.InvalidValueException;
import com.roadmap.exception.NotMatchedException;
import com.roadmap.exception.NullObjectException;
import com.roadmap.exception.ServerException;
import com.roadmap.repo.db.RdcQuery;

public class FriendshipManager extends BaseManager {
	private final static Logger _log = RdcLogger
			.getLogger(FriendshipManager.class);

	private final static String GROUP_ID_ALL = "0";
	private final static String GROUP_ID_OTHER = "-1";
	private final static int ALL_FRIENDS = -1;

	public FriendshipManager(String currentUser, String itemOwner) {
		super(currentUser, itemOwner, Friendship.class);
	}

	/**
	 * This action is not supported for the manager
	 */
	@Override
	public void create(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException,
			InvalidStatusException {
		throw new ActionNotSupportedException();
	}

	/**
	 * retrieve friends by given group.
	 * 
	 * @param groupId:
	 *            0 - get all friends; -1 - get ungrouped friends
	 */
	public List<FriendExt> getFriendsByGroup(String groupId)
			throws ServerException, NullObjectException, InvalidQueryException,
			InvalidValueException, NotMatchedException {
		// 获取所有好友
		if (groupId == null || groupId.equalsIgnoreCase(GROUP_ID_ALL)) {
			return getEntitiesOfOwner();
		}

		// 获取未分组好友
		if (groupId.equalsIgnoreCase(GROUP_ID_OTHER)) {
			return getUngroupedFriends();
		}

		// 获取分组里的好友
		Set<String> memberSet = getGroupMembers(groupId);
		if (memberSet == null || memberSet.isEmpty()) {
			return null;
		}

		// 查询特定分组好友
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_FRIENDS_WITH_NAME_BY_GROUP);
		query.addEntity("f", Friendship.class);
		query.setParameter("uid", getOwnerId());
		query.setParameterList("groups", memberSet.toArray());
		query.addScalar("realName", Hibernate.STRING);
		List results = query.getResults();

		return retrieveFriendsWithName(results, groupId);
	}

	/** 获取分组里所有好友ID */
	private Set<String> getGroupMembers(String groupId)
			throws NullObjectException, InvalidQueryException, ServerException,
			InvalidValueException {
		Category grp = (Category) getDbUtil().getEntityById(Category.class,
				groupId);
		if (grp == null) {
			return null;
		}
		return grp.getMemberIDSet();

	}

	private List<FriendExt> retrieveFriendsWithName(List results, String groupId) {
		if (results == null || results.isEmpty()) {
			return null;
		}

		List<FriendExt> friends = new ArrayList<FriendExt>();
		Iterator iter = results.iterator();
		while (iter.hasNext()) {
			Object[] result = (Object[]) iter.next();
			FriendExt ext = retrieveFriend(result, groupId);
			friends.add(ext);
		}

		return friends;
	}

	private FriendExt retrieveFriend(Object[] result, String groupId) {
		Friendship frd = (Friendship) result[0];
		FriendExt ext = new FriendExt();
		ext.setFriendId(getCorrectFriendId(frd));
		ext.setName((String) result[1]);
		if (groupId != null) {
			ext.setGroupId(groupId);
		}
		return ext;
	}

	/** 确定好友ID */
	private String getCorrectFriendId(Friendship friend) {
		if (friend.getId().equals(getOwnerId())) {
			return friend.getFriendId();
		}

		return friend.getId();
	}

	/**
	 * retrieve friends (a list of Aspirant instances) who are ungrouped
	 */
	private List<FriendExt> getUngroupedFriends() throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		Set<String> groupedIDs = getGroupedFriendIDs(null);
		String sql = null;
		boolean hasGroups = true;
		if (groupedIDs == null || groupedIDs.isEmpty()) {
			sql = RdcQueryConst.SQL_FRIENDS_ORDERBY_NAME;
			hasGroups = false;
		}
		else {
			sql = RdcQueryConst.SQL_FRIENDS_WITH_NAME_BY_GROUP;
		}

		RdcQuery query = getDbUtil().createNativeSQLQuery(sql);
		query.addEntity("f", Friendship.class);
		query.setParameter("uid", getOwnerId());
		query.addScalar("realName", Hibernate.STRING);
		if (hasGroups) {
			query.setParameterList("groups", groupedIDs.toArray());
		}
		List results = query.getResults();
		return retrieveFriendsWithName(results, GROUP_ID_OTHER);
	}

	/**
	 * get all grouped friends.
	 * 
	 * @param groupId:
	 *            if null, query on all groups
	 */
	private Set<String> getGroupedFriendIDs(String groupId)
			throws ServerException {
		// get all the friend groups
		String hql = null;
		Object[] params = null;
		if (groupId == null) {
			// get all groups
			hql = RdcQueryConst.HQL_ALL_GROUPS_BY_USER;
			params = new Object[] { getOwnerId(),
					Friendship.class.getSimpleName() };
		}
		else {
			// get the special
			hql =  RdcQueryConst.HQL_GROUP_BY_ID;
			params = new Object[] { groupId, getOwnerId() };
		}
		List groups = getDbUtil().executeHQLQuery(hql, params);
		return retrieveMembersFromGroups(groups);
	}

	/** 将分组里的好友ID放在set里 */
	private Set<String> retrieveMembersFromGroups(List groups) {
		if (groups == null || groups.isEmpty()) {
			return null;
		}

		Set<String> set = new HashSet<String>();
		Iterator<Category> iter = groups.iterator();
		while (iter.hasNext()) {
			String[] ids = iter.next().getMembersArray();
			if (ids == null) {
				continue;
			}

			for (String id : ids) {
				set.add(id);
			}
		}

		return set;
	}

	/**
	 * retrieve all friends of the user returned a list of Object[]{Friendship,
	 * Aspirant};
	 */
	@Override
	public List<FriendExt> getEntitiesOfOwner() throws ServerException,
			NullObjectException, NotMatchedException, InvalidValueException,
			InvalidQueryException {
		return getFriendsByNumber(ALL_FRIENDS);
	}

	@Override
	public List getExecutableEntities() throws HibernateException,
			ServerException, InvalidQueryException, InvalidValueException,
			NotMatchedException, ActionNotSupportedException {
		return getFriendsByNumber(BaseConst.MAX_RESULTS_IN_QUERY);
	}

	/** return the results with fixed number */
	private List<FriendExt> getFriendsByNumber(int num)
			throws NullObjectException, InvalidQueryException, ServerException,
			InvalidValueException {
		List results = retrieveFriendsFromDB(num);
		if (results == null || results.isEmpty()) {
			return null;
		}

		List<FriendExt> friends = new ArrayList<FriendExt>();
		Map<String, FriendExt> friendMap = new HashMap<String, FriendExt>();
		Iterator iter = results.iterator();
		while (iter.hasNext()) {
			Object[] result = (Object[]) iter.next();
			FriendExt ext = retrieveFriend(result, null);
			friends.add(ext);
			friendMap.put(ext.getFriendId(), ext);
		}

		// 确定每个好友所在的组
		List<Category> groups = retrieveAllGroupsFromDB();
		if (groups != null && !groups.isEmpty()) {
			for (Category group : groups) {
				setGroupForFriends(group, friendMap);
			}
		}
		return friends;
	}

	/** 从数据库查询好友 */
	private List retrieveFriendsFromDB(int num) throws ServerException {
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_FRIENDS_ORDERBY_NAME);
		query.addEntity("f", Friendship.class);
		query.addScalar("realName", Hibernate.STRING);
		query.setParameter("uid", getOwnerId());
		if (num > 0) {
			query.setMaxResults(num);
		}
		return query.getResults();
	}

	/** 为好友设置相应的分组 */
	private void setGroupForFriends(Category group, Map<String, FriendExt> fgMap) {
		if (group == null || fgMap == null) {
			return;
		}

		String[] fids = group.getMembersArray();
		if (fids == null) {
			return;
		}

		for (int i = 0; i < fids.length; i++) {
			FriendExt ext = fgMap.get(fids[i]);
			if (ext != null) {
				ext.setGroupId(fids[i]);
			}
		}
	}

	/** 获得所有好友分组 */
	public List retrieveAllGroupsFromDB() throws ServerException {
		Object[] params = { getOwnerId(), Friendship.class.getSimpleName() };
		return getDbUtil().executeHQLQuery(
				RdcQueryConst.HQL_ALL_GROUPS_BY_USER, params);
	}

	@Override
	public void delete(String entityId) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException {

		Friendship friend = retrieveFriendFromDB(entityId);
		Session session = getDbUtil().getSession();
		try {
			Category group = getGroupForFriend(session, friend);
			deleteFriend(session, friend, group);
		}
		catch (ServerException e) {
			throw e;
		}
		finally {
			session.close();
		}
	}

	/** 删除好友关系，并更新分组 */
	private void deleteFriend(Session session, Friendship friend, Category group)
			throws ServerException {
		// update the data
		Transaction tx = null;
		try {
			// create a query to delete specific friendship
			Query q1 = session
					.createQuery(RdcQueryConst.HQL_DELETE_FRIENDSHIP_BETWEEN_TWO_USERS);
			q1.setParameter("aid", friend.getId());
			q1.setParameter("bid", friend.getFriendId());

			// commit the updates
			tx = session.beginTransaction();
			q1.executeUpdate();
			if (group != null) {
				session.update(group);
			}
			tx.commit();
		}
		catch (HibernateException e) {
			_log.error("failed to delete friendship and update the group", e);
			if (tx != null) {
				tx.rollback();
			}
			throw new ServerException();
		}
	}

	private Friendship retrieveFriendFromDB(String friendId)
			throws InvalidQueryException, ServerException,
			InvalidValueException, EntityNotFoundException {

		BaseEntity friend = get(friendId);
		if (friend == null) {
			EntityNotFoundException e = new EntityNotFoundException();
			_log.error(String.format("%s doesn't return any friend entity.",
					friendId), e);
			throw e;
		}
		return (Friendship) friend;
	}

	private Category getGroupForFriend(Session session, Friendship friend) {
		if (friend == null) {
			return null;
		}

		// check which group the friend belongs to
		Query q = session.createQuery(RdcQueryConst.HQL_GROUP_BY_USER);
		q.setParameter(0, friend.getId());
		q.setParameter(1, Friendship.class.getSimpleName());
		q.setParameter(2, friend.getFriendId());
		// a friend can belong to only one group
		Category group = (Category) q.uniqueResult();
		if (group != null) {
			// delete the friend ID from the group
			String ids = group.getMemberIDs().replace(
					friend.getFriendId() + BaseConst.CATEGORY_ID_SEPARATOR, "");
			group.setMemberIDs(ids);
		}

		return group;
	}

	@Override
	public BaseEntity get(String entityId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		if (entityId == null) {
			InvalidValueException e = new InvalidValueException();
			_log.error("entity id is NULL", e);
			throw e;
		}
		Object[] params = { getCurrentUserId(), entityId, entityId,
				getCurrentUserId() };
		return (BaseEntity) getDbUtil().getUniqueEntity(
				RdcQueryConst.HQL_FRIENDSHIP_BETWEEN_TWO_USERS, params);
	}

	/** remove a friend from its group */
	public void removeFriendFromGroup(String friendId, String group) {
		// TODO
	}

	/** remove friends from its group */
	public void removeFriendsFromGroup(String[] friendIDs, String group) {
		// TODO
	}

	/** add a friend to a group */
	public void addFriendToGroup(String friendId, String group) {
		// TODO
	}

	/** add friends to a group */
	public void addFriendsToGroup(String[] friendIDs, String group) {
		// TODO
	}

	/** retrieve the group which the friend belongs to */
	public void getGroupByFriend(String friendId) {
		// TODO
	}

	/** move the friend from a group to another one */
	public void changeFriendGroup(String friendId, String fromGroup,
			String toGroup) {
		// TODO
	}
}